package com.flowable.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.flowable.common.result.CommonResult;
import com.flowable.converter.XmlConverter;
import com.flowable.service.ProcTaskService;
import org.flowable.bpmn.model.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 任务 实现
 */
@Service
public class ProcTaskServiceImpl implements ProcTaskService {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private XmlConverter xmlConverter;

    /**
     * 启动流程实例
     *
     * @param processDefinitionId 流程定义id
     * @param assignees           下一任务处理人(们)
     * @param extraData           额外信息
     * @return
     */
    @Override
    public CommonResult runProcess(String processDefinitionId, String[] assignees, String extraData) {
        // 根据当前任务获得下一任务的taskDefinitonKey
        String nextTaskDefinitionKey = getNextTaskDefinitionKey(processDefinitionId, null);
        // 根据userTaskId查找对应collection值
        String elKey = xmlConverter.getCollectionMap(processDefinitionId, nextTaskDefinitionKey);
        // 流程实例参数设置
        Map<String, Object> variables = new HashMap<>();
        variables.put(elKey, Arrays.asList(assignees));
        if (extraData != null) {
            variables.put("extraData", extraData);
        }
        // 启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, variables);
        JSONObject jsonObject = new JSONObject();
        // 流程定义的ID
        jsonObject.put("processDefinitionId", processInstance.getProcessDefinitionId());
        // 流程实例的ID
        jsonObject.put("processInstanceId", processInstance.getId());
        // 当前活动的ID
        //jsonObject.put("activityId", processInstance.getActivityId());
        return CommonResult.success(jsonObject);
    }

    /**
     * 查询指定用户的待办任务
     *
     * @param processDefinitionId 流程定义id
     * @param processInstanceId   流程实例id
     * @param taskAssignee        当前任务处理人
     * @return
     */
    @Override
    public CommonResult queryTask(String processDefinitionId, String processInstanceId, String taskAssignee) {
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (processDefinitionId != null) {
            taskQuery.processDefinitionId(processDefinitionId);
        }
        if (processInstanceId != null) {
            taskQuery.processInstanceId(processInstanceId);
        }
        if (taskAssignee != null) {
            taskQuery.taskAssignee(taskAssignee);
        }
        List<Task> taskList = taskQuery.orderByTaskCreateTime().asc().list();
        JSONObject jsonObject = new JSONObject();
        if (CollectionUtils.isEmpty(taskList)) {
            return CommonResult.failed("taskList is empty");
        }
        JSONArray jsonArray = new JSONArray();
        jsonObject.put("data", jsonArray);
        for (Task task : taskList) {
            JSONObject tmpJsonObject = new JSONObject();
            tmpJsonObject.put("processDefinitionId", task.getProcessDefinitionId());
            tmpJsonObject.put("processInstanceId", task.getProcessInstanceId());
            tmpJsonObject.put("taskId", task.getId());
            tmpJsonObject.put("taskName", task.getName());
            tmpJsonObject.put("assignee", task.getAssignee());
            tmpJsonObject.put("description", task.getDescription());
            jsonArray.add(tmpJsonObject);
        }
        return CommonResult.success(jsonObject);
    }

    /**
     * 完成当前任务，并提交到下一阶段任务
     *
     * @param processDefinitionId 流程定义id
     * @param processInstanceId   流程实例id
     * @param taskAssignee        当前任务处理人
     * @param assignees           下一任务处理人(们)
     * @param extraData           额外信息
     * @return
     */
    @Override
    public CommonResult completeTask(String processDefinitionId, String processInstanceId, String taskAssignee, String[] assignees, String extraData) {
        // 查询指定用户的代办任务
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (processDefinitionId != null) {
            taskQuery.processDefinitionId(processDefinitionId);
        }
        if (processInstanceId != null) {
            taskQuery.processInstanceId(processInstanceId);
        }
        Task task = taskQuery.taskAssignee(taskAssignee)
                .singleResult();
        if (task == null) {
            return CommonResult.failed("task is null");
        }
        // 根据当前任务获得下一任务的taskDefinitonKey
        String nextTaskDefinitionKey = getNextTaskDefinitionKey(processDefinitionId, task);
        // 根据userTaskId查找对应collection值
        String elKey = xmlConverter.getCollectionMap(processDefinitionId, nextTaskDefinitionKey);
        // 流程实例参数设置
        Map<String, Object> variables = new HashMap<>();
        if (elKey != null) {
            variables.put(elKey, Arrays.asList(assignees));
        }
        if (extraData != null) {
            variables.put(extraData, extraData);
        }
        try {
            // 完成任务
            taskService.complete(task.getId(), variables);
        } catch (Exception e) {
            return CommonResult.failed(e.getMessage());
        }
        String data = String.format("成功完成当前任务：%s, 下一任务：%s", task.getTaskDefinitionKey(), nextTaskDefinitionKey);
        return CommonResult.success(data);
    }

    /**
     * 回退任务，从源任务到目标任务
     *
     * @param processInstanceId 流程实例id
     * @param sourceTaskKey     源任务taskDefinitionKey
     * @param targetTaskKey     目标任务taskDefinitionKey，为空则返回到上一节点
     * @return
     */
    @Override
    public CommonResult rollbackTask(String processInstanceId, String sourceTaskKey, String targetTaskKey) {
        // 1.从历史任务实例中查源任务
        List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();
        List<String> htiTaskKeyList = htiList.stream().map(HistoricTaskInstance::getTaskDefinitionKey).collect(Collectors.toList());
        // 检查输入的sourceTaskKey是否有效
        if (!htiTaskKeyList.contains(sourceTaskKey)) {
            String data = String.format("sourceTaskKey: %s 错误", sourceTaskKey);
            return CommonResult.failed(data);
        }

        // 2.源任务中查找可以回退的目标任务列表
        boolean hasTargetTaskKey;
        if (targetTaskKey == null) {
            // 只查找上一个任务节点
            hasTargetTaskKey = true;
        } else {
            // 查找所有任务节点
            hasTargetTaskKey = false;
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        // 根据当前任务获得所有上级任务的taskKey
        List<String> preTaskKeyList = new ArrayList<>();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        getPreTaskKeyList(bpmnModel, sourceTaskKey, preTaskKeyList, hasTargetTaskKey);
        // 检查输入的targetTaskKey是否有效
        if (targetTaskKey != null && !preTaskKeyList.contains(targetTaskKey)) {
            String data = String.format("targetTaskKey: %s 错误", targetTaskKey);
            return CommonResult.failed(data);
        }
        targetTaskKey = Optional.ofNullable(targetTaskKey)
                .filter(preTaskKeyList::contains)
                .orElseGet(() -> preTaskKeyList.isEmpty() ? null : preTaskKeyList.get(0));

        // 3.从源任务回退到目标任务
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(processInstanceId)
                .moveActivityIdTo(sourceTaskKey, targetTaskKey)
                .changeState();
        String data = String.format("已成功从任务 %s 回退到任务 %s", sourceTaskKey, targetTaskKey);
        return CommonResult.success(data);
    }

    /**
     * 根据当前任务获得下一任务的taskDefinitonKey
     *
     * @param processDefinitionId
     * @param currentTask         当前任务
     * @return
     */
    private String getNextTaskDefinitionKey(String processDefinitionId, Task currentTask) {
        // 获取BPMN模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        String curTaskDefinitionKey = "";
        if (currentTask != null) {
            // 获取当前任务节点在BPMN模型中的ID，即taskDefinitionKey
            curTaskDefinitionKey = currentTask.getTaskDefinitionKey();
        } else {
            // 流程实例未启动，即无任务时，获取开始节点startEvent的ID
            FlowElement startEvent = bpmnModel.getMainProcess().getFlowElements().stream()
                    .filter(StartEvent.class::isInstance)
                    .map(StartEvent.class::cast)
                    .findFirst()
                    .orElse(null);
            // 获取开始事件的ID
            if (startEvent != null) {
                curTaskDefinitionKey = startEvent.getId();
            }
        }
        // 分析流程模型，找到当前任务的所有出站流
        FlowElement currentFlowElement = bpmnModel.getFlowElement(curTaskDefinitionKey);
        List<SequenceFlow> outgoingFlows = null;
        if (currentFlowElement instanceof FlowNode) {
            outgoingFlows = ((FlowNode) currentFlowElement).getOutgoingFlows();
        }

        // 确定下一个任务节点
        String nextTaskDefinitionKey = null;
        if (outgoingFlows != null && !outgoingFlows.isEmpty()) {
            // 确定哪一个是“下一个”任务节点
            // 直接取第一个出站流
            SequenceFlow sequenceFlow = outgoingFlows.get(0);
            FlowElement nextFlowElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef());
            //if (nextFlowElement instanceof UserTask) {
            nextTaskDefinitionKey = nextFlowElement.getId();
            //}
        }
        return nextTaskDefinitionKey;
    }

    /**
     * 根据当前任务获得所有上级任务的taskDefinitonKey
     *
     * @param bpmnModel        BPMN模型
     * @param curTaskKey       当前任务Key
     * @param preTaskKeyList   所有上级任务Key列表
     * @param hasTargetTaskKey 此参数为true，只查找上一级；此参数为false，查找所有上级
     */
    private void getPreTaskKeyList(BpmnModel bpmnModel, String curTaskKey, List<String> preTaskKeyList, boolean hasTargetTaskKey) {
        FlowElement flowElement = bpmnModel.getFlowElement(curTaskKey);
        FlowNode flowNode = (FlowNode) flowElement;
        SequenceFlow sequenceFlow = flowNode.getIncomingFlows().get(0);
        String preTaskKey = sequenceFlow.getSourceRef();
        if (!(bpmnModel.getFlowElement(preTaskKey) instanceof UserTask)) {
            return;
        }
        preTaskKeyList.add(preTaskKey);
        if (hasTargetTaskKey) {
            return;
        }
        getPreTaskKeyList(bpmnModel, preTaskKey, preTaskKeyList, false);
    }
}
